﻿using System;
using System.Collections.Generic;
using System.Web;
using System.Text.RegularExpressions;
using System.Threading;


namespace CCPSIP.Reg
{
    /// <summary>CCPSIP_001：使用者註冊 API (+國碼)</summary>
    /// UserType = 申請人類別。('CCP','Person','Customer','Vendor','Agent') 共五種，不分大小寫。
    /// DeviceType = 裝置類型代號。
    /// UserCountry = 申請人手機國碼。
    /// UserPhone = 申請人手機。
    /// UserID = 申請人員工編號 or 姓名。
    /// IDNCheck = 員工身份證字號後四碼。
    /// FriendCountry = iSender 朋友手機國碼。
    /// FriendPhone = iSender 朋友手機。
    /// FriendName = iSender 朋友姓名。
    /// Remark = 備註或公司名稱。

    public class register : IHttpHandler
    {
        private static string[] kAdminEmailList = {"hj_huang@ccp.com.tw",
                                                "mike_lin@ccp.com.tw", "chuck_lan@ccp.com.tw"
                                              };
        private const string kReportEmailAddr = "ccpsip_report@ccp.com.tw";

        private static List<string> kAllowUserTypes = new List<string>() { "CCP", "PERSON", "CUSTOMER", "VENDOR", "AGENT" };
        private const int kTryAgainMinute = 10;
        private const int kExpiryMinute = 30;

        private static char[] kPhoneTrimChars = { '\u202c' };


        public void ProcessRequest(HttpContext context)
        {
            HttpRequest req = context.Request;
            HttpResponse resp = context.Response;
            resp.ContentType = "text/plain";
            resp.ContentEncoding = System.Text.Encoding.UTF8;
            DateTime reqTime = DateTime.Now;
            string resultStr = "";

            try
            {
                //【01】取得並檢查POST參數
                //
                Dictionary<string, string> pDict = new Dictionary<string, string>();
                bool isAndroid = false;

                try
                {
                    pDict["FullName"] = null;//申請人姓名
                    pDict["UserType"] = req.Form["UserType"].ToUpper(); //申請人類別
                    pDict["DeviceType"] = req.Form["DeviceType"].Trim();//裝置類型代號
                    pDict["UserCountry"] = req.Form["UserCountry"];   //申請人手機國碼

                    pDict["UserPhone"] = req.Form["UserPhone"].Trim().Trim(kPhoneTrimChars);//申請人手機
                    pDict["UserID"] = req.Form["UserID"].Trim();//申請人員工編號或姓名
                    pDict["ID|Birth"] = req.Form["IDNCheck"];   //身分證字號後四碼 或 生日
                    pDict["FriendCountry"] = req.Form["FriendCountry"];   //iSender朋友手機國碼
                    pDict["FriendPhone"] = req.Form["FriendPhone"].Trim();//iSender朋友手機
                    pDict["FriendName"] = req.Form["FriendName"].Trim();  //iSender朋友姓名
                    pDict["Remark"] = req.Form["Remark"].Trim();//備註或公司名稱

                    //清理手機門號
                    pDict["UserPhone"] = ChrisKit.CleanPhoneNum(pDict["UserPhone"]);
                    pDict["FriendPhone"] = ChrisKit.CleanPhoneNum(pDict["FriendPhone"]);

                    if (!kAllowUserTypes.Contains(pDict["UserType"]) || pDict["UserID"].Length < 2
                         || pDict["FriendName"].Length < 2)
                        throw new Exception("Error_01: Parameter error.");

                    if (pDict["DeviceType"].StartsWith("Android "))
                        isAndroid = true;

                    string[] UserLocale;
                    if (isAndroid)
                    {
                        string[] separator1 = { "_" };
                        UserLocale = req.Form["UserLocale"].Split(separator1, StringSplitOptions.None);

                        string[] separator2 = { "; " };
                        string[] typeArray = req.Form["DeviceType"].Split(separator2, StringSplitOptions.None);
                        pDict["OSVer"] = typeArray[0];
                        pDict["DeviceType"] = typeArray[1];
                    }
                    else
                    {
                        string[] separator1 = { "; " };
                        UserLocale = req.Form["UserLocale"].Split(separator1, StringSplitOptions.None);

                        Match browserMatch = Regex.Match(req.UserAgent, @"(Firefox|Chrome|MSIE|Trident)");
                        if (browserMatch.Success)
                            pDict["OSVer"] = browserMatch.Value;
                        else
                            pDict["OSVer"] = "iOS " + AppleKit.GetIOSVersion(req.UserAgent);
                    }

                    if (UserLocale.Length >= 1)
                        pDict["LangID"] = UserLocale[0];  //語言設定
                    if (UserLocale.Length >= 2)
                        pDict["RegionID"] = UserLocale[1];//地區設定
                }
                catch
                {
                    throw new Exception("Error_01:Parameter Error");//參數錯誤
                }


                //【01-1】國碼前處理
                //
                if (string.IsNullOrEmpty(pDict["UserCountry"]))
                {
                    pDict["UserCountry"] = "886";//預設台灣
                }
                else if (pDict["UserCountry"].StartsWith("+"))
                {
                    pDict["UserCountry"] = pDict["UserCountry"].Substring(1);//去開頭"+"
                }

                if (string.IsNullOrEmpty(pDict["FriendCountry"]))
                {
                    pDict["FriendCountry"] = "886";//預設台灣
                }
                else if (pDict["FriendCountry"].StartsWith("+"))
                {
                    pDict["FriendCountry"] = pDict["FriendCountry"].Substring(1);//去開頭"+"
                }

                if (pDict["UserPhone"].StartsWith("0"))
                {
                    pDict["UserPhone"] = pDict["UserPhone"].Substring(1);//去開頭"0"
                }
                if (pDict["FriendPhone"].StartsWith("0"))
                {
                    pDict["FriendPhone"] = pDict["FriendPhone"].Substring(1);//去開頭"0"
                }

                pDict["IPAddr"] = ChrisKit.getIPAddress(req);
                pDict["ErrorMsg"] = null;//錯誤訊息
                pDict["UserAgent"] = ChrisKit.Substr(req.UserAgent, 0, 255);//[Debug]

                if (pDict["UserCountry"] == "886")
                {
                    //【01-2】檢查台灣手機格式
                    //
                    if (!Regex.IsMatch(pDict["UserPhone"], "^9[0-9]{8}$"))
                    {
                        string mailSubject = "【通報】iSender 註冊: 手機格式無效";
                        string mailBody = ChrisKit.DictToJSON(pDict, true);
                        ChrisKit.SendCCPEmail(kReportEmailAddr, kAdminEmailList, null, mailSubject, mailBody);
                        throw new Exception("Error_01:Phone format is invalid.");//手機格式無效
                    }
                }


                //【02】檢查註冊錯誤(阻擋DoS,暴力測試)
                //
                DateTime allowTime = reqTime.AddHours(-1);
                UInt32 errorCount = OracleKitReg.CountApplyError(pDict["IPAddr"], allowTime);
                if (errorCount >= 10)
                {
                    pDict["ErrorMsg"] = "Too many fails, please try again next hour.";//失敗太多次
                    if (errorCount == 10)
                    {
                        OracleKitReg.InsertRegApply(pDict);
                        string mailBody = ChrisKit.DictToJSON(pDict, true);
                        ChrisKit.SendCCPEmail(kReportEmailAddr, kAdminEmailList, null, "【通報】iSender 註冊失敗", mailBody);
                    }
                    throw new Exception("Error_02:" + pDict["ErrorMsg"]);
                }


                //【03】檢查身分
                //
                if (pDict["UserType"] == "CCP")
                {
                    pDict["UserID"] = pDict["UserID"].ToUpper();

                    string EmpID = pDict["UserID"];
                    bool isValid = OracleKit.ValidateIDN(EmpID, pDict["ID|Birth"]);
                    if (!isValid)
                        isValid = OracleKit.ValidateBirth(EmpID, pDict["ID|Birth"]);

                    if (!isValid)
                    {
                        pDict["ErrorMsg"] = "員工編號或身分證後四碼錯誤";
                        OracleKitReg.InsertRegApply(pDict);
                        throw new Exception("Error_03:" + pDict["ErrorMsg"]);
                    }

                    pDict["FullName"] = OracleKitUser.getEmpName(EmpID);

                    if (string.IsNullOrEmpty(pDict["FullName"]))
                    {
                        pDict["FullName"] = OracleKit.getFullName(EmpID);

                        if (string.IsNullOrEmpty(pDict["FullName"]))
                        {
                            pDict["ErrorMsg"] = "找不到您的資料，請向資訊人員確認";
                            throw new Exception("Error_03:" + pDict["ErrorMsg"]);
                        }
                    }

                    //【03-1】檢查員工身分
                    //
                    string CCPExtHQ = SipKit.getCCPExtHQ(EmpID);
                    if (!string.IsNullOrEmpty(CCPExtHQ))
                        pDict["UserType"] = "CCP_HQ";
                    else if (OracleKitUser.isAbroadEmp(EmpID))
                        pDict["UserType"] = "CCP_ABROAD";
                }
                else
                {
                    pDict["FullName"] = ChrisKit.Substr(pDict["UserID"], 0, 30);
                    pDict["UserID"] = null;
                }
                //【04】檢查關係人的身分權限
                //

                string FriendUserType = OracleKitUser.getUserType(pDict["FriendCountry"], pDict["FriendPhone"], pDict["FriendName"]);

                if (string.IsNullOrEmpty(FriendUserType))
                {
                    pDict["ErrorMsg"] = string.Format("{0} does not use iSender, or the phone number is wrong.", pDict["FriendName"]);//姓名或門號錯誤
                    OracleKitReg.InsertRegApply(pDict);
                    throw new Exception("Error_04:" + pDict["ErrorMsg"]);
                }
                else if (pDict["UserType"] != "PERSON")
                {
                    if (!SipKit.isCCPUser(FriendUserType))
                    {
                        pDict["ErrorMsg"] = string.Format("{0} has no right to confirm your identity", pDict["FriendName"]);//權限不足
                        throw new Exception("Error_04:" + pDict["ErrorMsg"]);
                    }
                }

                //【05】檢查短時間內連續註冊
                //
                allowTime = reqTime.AddMinutes(-1 * kTryAgainMinute);
                UInt32 authCount = OracleKitReg.CountRegAuth(pDict["IPAddr"], allowTime);
                if (authCount > 3)
                {
                    pDict["ErrorMsg"] = string.Format("Error_05:Register too many times, please try again after {0} minutes.", kTryAgainMinute);//短時間內連續註冊
                    throw new Exception(pDict["ErrorMsg"]);
                }


                //【To-Do】檢查現用裝置
                //Example Push: "您已經在 iPhone 5, iPad 3, iPad mini 上使用iSender，......將取代該裝置的權限，請注意。"
                //

                //【99】存檔，發送門號認證簡訊
                //
                UInt32 applyID = OracleKitReg.InsertRegApply(pDict);

                string authCode = RNG.Next(100000, 999999).ToString();
                DateTime expiryTime = DateTime.Now.AddMinutes(kExpiryMinute);

                OracleKitReg.InsertRegAuth(applyID, authCode, expiryTime);

                string authMsg = string.Format("Auth Code:{0} (iSender Service, please verify before {1:M/d HH:mm} UTC{1:zz}.)", authCode, expiryTime);


                //HiAir SMS上限：中文53(UTF-8)/純英數字159
                //
                string MobileNum = string.Format("{0}{1}", pDict["UserCountry"], pDict["UserPhone"]);
                string HiAirResp = SmsKit.SendHiAirSMS(MobileNum, authMsg);
                if (!string.IsNullOrEmpty(HiAirResp))
                {
                    string HiAirID = HiAirResp;
                    OracleKitReg.UpdateRegAuthHiNetID(applyID, HiAirID);

                    AuthObject authObj = new AuthObject(applyID, HiAirID, MobileNum, authMsg);
                    new Thread(new ParameterizedThreadStart(CheckSMSSent)).Start(authObj);
                }


                //【Debug】Start
                pDict.Remove("ID|Birth");
                authMsg += Environment.NewLine;
                authMsg += ChrisKit.DictToJSON(pDict, true);
                authMsg = "（這是一封SMS簡訊）" + Environment.NewLine + authMsg;
                ChrisKit.SendCCPEmail(kReportEmailAddr, kAdminEmailList, null, "【Debug】iSender 認證碼", authMsg);
                //【Debug】End


                resultStr = "OK";
            }
            catch (Exception ex)
            {
                resp.ContentType = "application/xml";
                resultStr = ChrisKit.Ex2Xml(ex, false);
            }
            finally
            {
                int contentLength = resp.ContentEncoding.GetByteCount(resultStr);
                resp.AppendHeader("Content-Length", contentLength.ToString());
                resp.Write(resultStr);
                resp.Flush();
                resp.Close();
            }
        }


        public bool IsReusable
        {
            get
            {
                return false;
            }
        }


        /// <summary>
        /// 等待一小段時間後，查詢SMS傳送結果。
        /// 若簡訊被阻擋，則用備援Service再傳一次。
        /// </summary>
        private void CheckSMSSent(object param)
        {
            if (!(param is AuthObject))
                return;

            Thread.Sleep(10 * 1000);

            for (int i = 0; i < 3; i++)
            {
                if (i > 0)
                {
                    Thread.Sleep(60 * 1000);
                }

                try
                {
                    AuthObject authObj = (AuthObject)param;
                    KeyValuePair<short, string>? result = SmsKit.QueryHiAirSMS(authObj.HiAirID);
                    if (!result.HasValue)
                    {
                        continue;
                    }

                    short HiAirCode = result.Value.Key;
                    string HiAirDesc = result.Value.Value;
                    OracleKitReg.UpdateRegAuthHiNet(authObj.ApplyID, HiAirCode, HiAirDesc);

                    if (HiAirCode == 1 || HiAirCode == 2 || HiAirCode == 4)
                    {
                        // 1 = Mobile turn off/Mobile out of scope
                        // 2 = DetailCode:[9] -> System contains no data
                        // 4 = Has send to SMC, query no complete
                        continue;
                    }
                    else if (HiAirCode == 23)
                    {
                        // 23=Message is barred
                        // 用備援服務發送簡訊
                        string SmsgoID = SmsKit.SendSmsgoSMS(authObj.MobileNum, authObj.AuthMsg);
                        if (!string.IsNullOrEmpty(SmsgoID))
                        {
                            OracleKitReg.UpdateAltSMSResp(authObj.ApplyID, SmsgoID);
                        }
                    }

                    // 查詢HiAir之前尚未確認結果的記錄
                    OracleKitReg.CheckHiAirSent();

                    break;
                }
                catch
                {
                }
            }//End of for loop
        }


        /// <summary>
        /// 認證簡訊暫存物件
        /// </summary>
        private class AuthObject
        {
            private UInt32 _ApplyID;//註冊資料ID
            private string _HiAirID;//HiAir MessageID
            private string _MobileNum;//手機門號(含國碼)
            private string _AuthMsg;  //認證簡訊


            public UInt32 ApplyID { get { return this._ApplyID; } }
            public string HiAirID { get { return this._HiAirID; } }
            public string MobileNum { get { return this._MobileNum; } }
            public string AuthMsg { get { return this._AuthMsg; } }


            public AuthObject(UInt32 applyID, string hiAirID, string mobileNum, string authMsg)
            {
                _ApplyID = applyID;
                _HiAirID = hiAirID;
                _MobileNum = mobileNum;
                _AuthMsg = authMsg;
            }
        }


    }
}
